#!/usr/bin/python
#
"""
A module to rename media files based on their content numbers.

AV Renamer is a module to homogenize media files for future parsing and
indexing.  It relies on common assumptions about naming conventions, roughly
that for a filename where the artist/title (%a), song number/show number/air
date (%n) and song title/content title (%c) are all present they occur in this
order: %a, %n, %c.  With some punctuation delimiting them.

AV Renamer currently relies on all files it will process to have content
numbers to determine %a and %c most effectively.  A feature is planned to
guess at where %a ends and %c begins but it is not yet in testing.
"""

__author__ = 'Daniel Wilcox <dmwilcox@gmail.com>'

import common
import os
import avfilename

DEFAULT_PREFS = dict(
                     verbose=2,
                     recursive=False,
                     dir='.',
                     output_model='%a-%n-%c',
                     delimiter='-',
                     interactive=True,
                     blacklist=None,
                     pretend=False,
                     artist_override=None
                    )

CONFIG_FILE = os.sep.join(['%s' % os.environ['HOME'],
                           '.mediaark',
                           'av_renamer.yaml'])

module_logger = common.setupModuleLogger(__name__)


def getFileList(directory, recursive=False):
  """Walks directory structure finds media files and returns a list of strings."""
  file_list = []
  if not recursive:
    dirpath, _, filenames = os.walk(directory).next()
    for file in filenames:
      if avfilename.hasMediaExt(file):
        file_list.append(os.path.join(dirpath, file))
  else:
    for dirpath, _, filenames in os.walk(directory): 
      for file in filenames:
        if avfilename.hasMediaExt(file):
          file_list.append(os.path.join(dirpath, file))
  module_logger.debug('Found files: %s' % file_list)
  return file_list


def getFormatedNames(file_list,
                     output_model,
                     blacklist=None,
                     verbose=20,
                     delim=' ',
                     artist_override=None):
  """Declare AVFilenameWithNumber Objs to see what can get by without
     user intervention, return a dict{} of old->Obj.name_out filenames.
  """
  verbose = (verbose % 6) * 10
  #new_names=dict.fromkeys(file_list)
  new_names = {}
  for filepath in file_list:
    directory, name = os.path.split(filepath)
    try:
      av_file_obj = avfilename.AVFileWithNumber(name, directory, 
                                                blacklist=blacklist,
                                                verbose=verbose,
                                                delim=delim,
                                                artist_override=artist_override)
    except: # Raise a specific exception so this isn't so broad.
      module_logger.warn('Cannot parse %s in %s, skipping.' % (name,
                                                               directory))
      continue
    av_file_obj.parseFilename()
    #new_names[filepath] = av_file_obj.fullNameOut(output_model)
    new_names[filepath] = av_file_obj.fullPathOut(output_model)
    #print 'setting %s -> %s' % (filepath, new_names[filepath])
  return new_names


def interactiveMove(names_dict, pretend=True, interactive=True, fullname=False):
  """Presents a list of proposed changes to be made to filenames and asks
     for changes or confirmation.  Allow confirmation on select groups for ex:
     1-60, and widdle down list and ask for models for remaining.
  """
  #for src, dst in names_dict.items():
  #  print '%s -> %s' % (src, dst)
  if pretend or interactive:
    for key in names_dict:
      if not names_dict[key] or not key:
        raise Exception, 'empty dest. path on %s -> %s' % (key, names_dict[key])
      if fullname:
        print '%s --> %s' % (key, names_dict[key])
      else:
        _, src = os.path.split(key)
        _, dst = os.path.split(names_dict[key])
        print '%s --> %s' % (src, dst)
  if pretend:
    return
  elif interactive:
    response = raw_input('Rename files? (y/n):')
    response = response.lower()
    if not response.startswith('y'):
      return
  for key in names_dict:
    try:
      os.rename(key, names_dict[key])
    except OSError:
      module_logger.warning('Could not rename %s -> %s' % (key,
                                                           names_dict[key]))


def main(options):
  module_logger = common.setupModuleLogger(__name__)
  # Get all user preference stuff...
  compiled_config = {}
  if options.conf_file:
    conf_file = options.conf_file
  else:
    conf_file = CONFIG_FILE  
  compiled_config = common.GetYamlDictFromFile(conf_file) or {}
  module_logger.debug('YAML Conf data: %s' % compiled_config)

  # Merge command line options last to use as overrides.
  option_dict = common.ObjAttrsToDict(options, DEFAULT_PREFS.keys())
  compiled_config = common.MergeDicts(DEFAULT_PREFS, compiled_config)
  module_logger.debug('Default+Config prefs: %s' % compiled_config)
  compiled_config = common.MergeDicts(compiled_config, option_dict)
  module_logger.debug('Default+Config+CmdLine prefs: %s' % compiled_config)

  # Setup module logging
  module_logger.setLevel((compiled_config['verbose'] % 6) * 10)

  # Start main operation
  file_list = getFileList(compiled_config['dir'],
                          recursive=compiled_config['recursive'])
  rename_dict = getFormatedNames(
      file_list,
      compiled_config['output_model'],
      blacklist=compiled_config['blacklist'],
      verbose=compiled_config['verbose'],
      delim=compiled_config['delimiter'],
      artist_override=compiled_config['artist_override'])

  interactiveMove(rename_dict,
                  pretend=compiled_config['pretend'],
                  interactive=compiled_config['interactive'])


if __name__ == '__main__':
  """ Need options for 'fixed model modes' wherein the user specifies the
      input or output filename models to parse on.  Recursive also needs to
      be an option and so does the blacklist.
  """
  import sys
  from optparse import OptionParser
  parser = OptionParser()

  parser.add_option('-d', '--dir', action='store', type='string', dest='dir',
                    help=('Directory to look for media files in. Default: '
                          'Current directory.'))
  parser.add_option('-b', '--blist', action='store', type='string', 
                    dest='blacklist', help='File of substrings to remove.')
  parser.add_option('-c', '--config', action='store', type='string',
                    dest='conf_file', help=('Path to a config YAML config '
                        'file.  Default: %s' % CONFIG_FILE))
  parser.add_option('-o', '--output', action='store', type='string', 
                    dest='output_model', help=('Filename output model. '
                         '%a = Artist/Show, %n = Episode/Song Number, %c = '
                         'Episode/Song.  Default: %a-%n-%c'))
  parser.add_option('-s', '--separator', action='store', type='string',
                    dest='delimiter', help=('Filename output delimiter. '
                         'Default: \'-\' '))
  parser.add_option('-v', '--verbose', action='store', type='int',
                    dest='verbose', help=('Toggle verbose output, 0=debug -> '
                                          '5=critical. Default: 2 (info).'))
  parser.add_option('-r', '--recursive', action='store_true', dest='recursive',
                    help=('Recusively parse directories for files. '
                          'Default: False.'))
  parser.add_option('-n', '--nointeract', action='store_false', 
                    dest='interactive',
                    help='Do not prompt for final rename. Default: True')
  parser.add_option('-p', '--pretend', action='store_true', dest='pretend',
                    help=('Pretend mode.  Do not rename files. Default: False.'))
  parser.add_option('-a', '--artist_override', action='store', type='string',
                    dest='artist_override',
                    help=('Override the Show/Artist name detected. Ex: '
                          '-a TheColbertReport or -a "The Colbert Report"'))

  (options, args) = parser.parse_args()

  main(options)
